import logging, json,requests, os, inspect
from datetime import datetime
from zoneinfo import ZoneInfo
from time import sleep
from requests.auth import HTTPBasicAuth
from os.path import expanduser

host = "https://api.worldquantbrain.com/"
alpha_fail_attempt_tolerance = 15 # 每个alpha允许的最大失败尝试次数

def init_logging(filename= ""):
    if filename == "" :
        caller_frame = inspect.stack()[1]
        filename = caller_frame.filename if caller_frame else "unknown"
        filename = os.path.splitext(os.path.basename(filename))[0] + ".log"
    cst_time = datetime.now(ZoneInfo("Asia/Shanghai")).strftime("%Y%m%d") #China time
    base_name, file_extension = os.path.splitext(filename)

    logging.basicConfig(  
        handlers=[  
            logging.FileHandler(f"./logs/{base_name}_{cst_time}{file_extension}"),  # 文件处理器  
            logging.StreamHandler()                      # 控制台处理器  
        ], 
        level=logging.INFO,  
        format='%(asctime)s - %(levelname)s - %(message)s'  
    ) 
    return filename

def sign_in():
    # Load credentials # 加载凭证
    with open(expanduser('brain.txt')) as f:
        credentials = json.load(f)

    # Extract username and password from the list # 从列表中提取用户名和密码
    username, password = credentials

    # Create a session object # 创建会话对象
    sess = requests.Session()

    # Set up basic authentication # 设置基本身份验证
    sess.auth = HTTPBasicAuth(username, password)

    # Send a POST request to the API for authentication # 向API发送POST请求进行身份验证
    response = sess.post('https://api.worldquantbrain.com/authentication')

    # logging response status and content for debugging # 打印响应状态和内容以调试
    logging.info(f"login for {username}, return code: {response.status_code}")
    return sess

def getAlphaList(sess, sharpeMin="",limit=100, startIdx=0, status="UNSUBMITTED"):
    sharpe = f"&is.sharpe%3E={sharpeMin}" if len(sharpeMin) > 0 else ""

    listUrl = f"{host}users/self/alphas?limit={limit}&offset={startIdx}&status={status}%1FIS_FAIL{sharpe}&order=dateCreated&hidden=false"
    return sess.get(listUrl)

def getAlpha(sess,alphaId):
    listUrl = f"{host}alphas/{alphaId}"
    return sess.get(listUrl)

def pathAlpha(sess,alphaId, data):
    listUrl = f"{host}alphas/{alphaId}"
    return sess.patch(listUrl, json=data)

def checkAlpha(sess,alphaId): #return Alpha Valid or not
    listUrl = f"{host}alphas/{alphaId}/check"
    while True:
        response = sess.get(listUrl)
        retry_after_sec = float(response.headers.get("Retry-After", 0))
        if retry_after_sec == 0:  # check done!
            break
        sleep(retry_after_sec)
    checks = response.json()
    hasFail = any(check["result"] == "FAIL" for check in checks["is"]["checks"])
    return not hasFail

#todo, need modify
def submitAlpha(sess,alphaId, data):
    listUrl = f"{host}alphas/{alphaId}"
    return sess.patch(listUrl, json=data)

def runSimulate(sess, alpha, index, logFile):
    if not logging.getLogger().handlers:  
        init_logging(logFile) #多线程会丢失loging上下文, 重新初始化，必须带 logfile
    keep_trying = True  # 控制while循环继续的标志
    failure_count = 0  # 记录失败尝试次数的计数器
    while keep_trying:
        try:
            # 尝试发送POST请求
            sim_resp = sess.post(
                f'{host}simulations',
                json=alpha  # 将当前alpha（一个JSON）发送到服务器
            )

            # 从响应头中获取位置
            sim_progress_url = sim_resp.headers['Location']
            while True: #wait the simulate finish.
                sim_progress_resp = sess.get(sim_progress_url)
                retry_after_sec = float(sim_progress_resp.headers.get("Retry-After", 0))
                if retry_after_sec == 0:  # simulation done!模拟完成!
                    break
                sleep(retry_after_sec)

            alpha_id = sim_progress_resp.json()["alpha"] 

            logging.info(f'Alpha #{index}, id: {alpha_id} location is: {sim_progress_url}')  # 记录位置
            keep_trying = False  # 成功获取位置，退出while循环

        except Exception as e:
            # 处理异常：记录错误，让程序休眠15秒后重试
            if failure_count == 0:
                logging.error(f"Alpha #{index}: No Location , will keep retry (total:{alpha_fail_attempt_tolerance}), error message: {str(e)}")
            sleep(15)  # 休眠15秒后重试
            failure_count += 1  # 增加失败尝试次数

            # 检查失败尝试次数是否达到容忍上限
            if failure_count >= alpha_fail_attempt_tolerance:
                #sess = sign_in()  # 重新登录会话, no need now.
                failure_count = 0  # 重置失败尝试次数
                logging.error(f"Alpha #{index}: No location for too many times, move to next alpha. Current: {alpha['regular']}")  # 记录错误
                break  # 退出while循环，移动到for循环中的下一个alpha